package instrumentation;
import instrumentation.Instrumenter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
import java.util.HashSet;
import soot.ArrayType;
import soot.Body;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.IntegerType;
import soot.Local;
import soot.LongType;
import soot.Modifier;
import soot.SootMethod;
import soot.NullType;
import soot.PatchingChain;
import soot.PrimType;
import soot.RefLikeType;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Trap;
import soot.Type;
import soot.Unit;
import soot.ValueBox;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.CastExpr;
import soot.jimple.CmpExpr;
import soot.jimple.CmpgExpr;
import soot.jimple.CmplExpr;
import soot.jimple.ConditionExpr;
import soot.jimple.DefinitionStmt;
import soot.jimple.DoubleConstant;
import soot.jimple.EnterMonitorStmt;
import soot.jimple.EqExpr;
import soot.jimple.ExitMonitorStmt;
import soot.jimple.Expr;
import soot.jimple.FieldRef;
import soot.jimple.FloatConstant;
import soot.jimple.GeExpr;
import soot.jimple.GotoStmt;
import soot.jimple.GtExpr;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.LeExpr;
import soot.jimple.LongConstant;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.LtExpr;
import soot.jimple.NeExpr;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.NullConstant;
import soot.jimple.RetStmt;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.StaticFieldRef;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.VirtualInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.jimple.TableSwitchStmt;
import soot.jimple.ThisRef;
import soot.jimple.ThrowStmt;
import soot.jimple.internal.JArrayRef;
import soot.jimple.internal.JInstanceFieldRef;
import soot.jimple.internal.JLengthExpr;
import soot.jimple.internal.JimpleLocal;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.toolkits.scalar.CombinedAnalysis;
import soot.toolkits.scalar.CombinedDUAnalysis;
import soot.toolkits.scalar.UnitValueBoxPair;
import soot.util.Chain;

import instrumentation.Instrumenter.UnifiedInstrumentator;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;

import Operations.ValuesGenerator;
import Operations.characterGenerator;

public class WhiteInstrumenter implements UnifiedInstrumentator{
	private static final Logger logger = Logger.getLogger("WhiteInstrumenter");

	private static final SootClass trackerClass;
	private static SootMethod trackerSingleton;
	private static SootMethod getConditionTargetId;
	private static final SootMethod setConditionTargetDistance;
	private static final SootMethod calculateConditionTargetDistance;
//
	private static final SootMethod branchHit;
	
	private static final SootMethod branchCovered;
	private static final SootMethod setIfCondition;
//	private static final SootMethod methodBranchEnd;	
	

	static {

		final String TRACKER = TrackerCoverage.class.getName();
		Scene.v().loadClassAndSupport(TRACKER);
		trackerClass = Scene.v().getSootClass(TRACKER);
		trackerSingleton = trackerClass.getMethodByName("getTracker");
		
		// Control-Flow Graph stuff
		getConditionTargetId = trackerClass.getMethodByName("getConditionTargetId");
  	    setConditionTargetDistance = trackerClass.getMethodByName("setConditionTargetDistance");
		calculateConditionTargetDistance = trackerClass.getMethodByName("calculateConditionTargetDistance");
		branchHit = trackerClass.getMethodByName("branchHit");
//		
		
		branchCovered = trackerClass.getMethodByName("branchCovered");

		setIfCondition = trackerClass.getMethodByName("setIfCondition");
//		methodBranchEnd = trackerClass.getMethodByName("methodBranchEnd");
	}
	public WhiteInstrumenter() {
		System.out.println("White-Box instrumenter loaded");
	}
	private Analyzer analyzer;
	private Local localTracker;
	private Local localConditionTarget;
	private Local localTmpDouble1;
	private Local localTmpDouble2;
	
	private static int branchId=1;
	private int falseBranch;
	private int trueBranch;
	private String mName;
	private String cName;
	private String fieldRef,fieldRef_1,fieldRef_2,cValue,cValue_1,cValue_2,mCall,mClass,callee,callee_1,callee_2;
	private String args,argsField_1,argsField_2;
	private boolean methodPublic=true,isReturnTpeBool;
	private Type typeOp1,typeOp2;
	@Override
	public int init(Body oldBody, Body newBody, Chain<Unit> newUnits, IdentityStmt[] paramDefs) {
		// some useful constants
		final SootMethod method = newBody.getMethod();
		final SootClass sClass = method.getDeclaringClass();
		cName=sClass.getName();
		mName=method.getName();

		localTracker = Jimple.v().newLocal("__tracker__", trackerClass.getType());
		newBody.getLocals().add(localTracker);
		newUnits.add(Jimple.v().newAssignStmt(localTracker, Jimple.v().newStaticInvokeExpr(trackerSingleton.makeRef())));

		localConditionTarget = Jimple.v().newLocal("condition_target__", IntType.v());
		newBody.getLocals().add(localConditionTarget);
	
//		newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef())));

		localTmpDouble1 = Jimple.v().newLocal("_tmp_double_1__", DoubleType.v());
		newBody.getLocals().add(localTmpDouble1);

		localTmpDouble2 = Jimple.v().newLocal("_tmp_double_2__", DoubleType.v());
		newBody.getLocals().add(localTmpDouble2);
		

		
		analyzer = new Analyzer(newUnits, newBody, oldBody, paramDefs);
		return branchId;

	}
	

	@Override
	public void processPre(Chain<Unit> newUnits, Stmt op) {
		analyzer.process(newUnits, op);
	}
	@Override
	public int processPost(Chain<Unit> newUnits, Stmt op) {
		analyzer.processPost(newUnits, op);
		return branchId;
	}


	public enum DataType {
		Boolean, Character, Number, String, Array, Reference
	}

	class Analyzer {
		private final Set<Unit> deadCode;
		private final CombinedAnalysis duAnalysis;
		private final Map<Local, Data> localRepository;

		
		/** link the first unit of each building block to the set of incoming edges (to complete) */
		private final Map<Unit, Set<Integer>> toLinkMap;
		private final Set<Integer> branchUsedSwitch;
		
		public Analyzer(Chain<Unit> newUnits, Body newBody, Body oldBody, IdentityStmt[] paramDefs) {
			final SootMethod method = newBody.getMethod();
//			final String methodName = method.getName();
//			final boolean methodStatic = method.isStatic();
			 methodPublic = method.isPublic();
//			final boolean methodPrivate = method.isPrivate();
			
			deadCode = new HashSet<Unit>();
			
			toLinkMap = new HashMap<Unit, Set<Integer>>();
			branchUsedSwitch=new HashSet<Integer>();
			
			final UnitGraph unitGraph = new ExceptionalUnitGraph(oldBody);
			duAnalysis = CombinedDUAnalysis.v(unitGraph);
			
			localRepository = new HashMap<Local, Data>();
			
			
		}

		private void preProcess(Chain<Unit> newUnits, Stmt stmt) {
			fieldRef="";cValue="";mCall="";mClass="";
			
			if(stmt instanceof IfStmt){
				falseBranch=branchId++;
				trueBranch=branchId++;

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(trueBranch))));
			}
		}
		public void process(Chain<Unit> newUnits, Stmt op) {
			preProcess(newUnits, op);
	//		System.out.println(op);
			if(op.containsInvokeExpr())
				processInvokeExpr(newUnits,(Stmt)  op);	
			if(op instanceof AssignStmt)
				process(newUnits, (AssignStmt) op);
			else if(op instanceof GotoStmt)
				process(newUnits, (GotoStmt) op);
			else if(op instanceof IdentityStmt)
				process(newUnits, (IdentityStmt) op);
			else if(op instanceof InvokeStmt)
				;//process(newUnits, (InvokeStmt) op); // nothing to do
			else if(op instanceof IfStmt)
				process(newUnits, (IfStmt) op);
			else if(op instanceof LookupSwitchStmt)
				process(newUnits, (LookupSwitchStmt) op);
			else if(op instanceof TableSwitchStmt)
				process(newUnits, (TableSwitchStmt) op);
			else if(op instanceof RetStmt)
				process(newUnits, (RetStmt) op);
			else if(op instanceof ReturnStmt)
				process(newUnits, (ReturnStmt) op);
			else if(op instanceof ReturnVoidStmt)
				process(newUnits, (ReturnVoidStmt) op);
			else if(op instanceof ThrowStmt)
				process(newUnits, (ThrowStmt) op);
			else if(op instanceof EnterMonitorStmt)
				; // nothing to do
			else if(op instanceof ExitMonitorStmt)
				; // nothing to do
			else {
				logger.info("cannot analyze " + op + " (" + op.getClass().getName() + ")");
			}
		}
		public void processInvokeExpr(Chain<Unit> newUnits, Stmt op) {
		InvokeExpr iexpr = (InvokeExpr)op.getInvokeExpr();
		    if (iexpr instanceof SpecialInvokeExpr)
		    {
		        SootMethod target = ((SpecialInvokeExpr)iexpr).getMethod();
		        if(!target.getName().equals("<init>"))
		        if(!Instrumenter.calls.contains(target.getName()))
		        		Instrumenter.calls.add(new String(target.getName()));
		    }
		    if (iexpr instanceof StaticInvokeExpr)
		    {
		        SootMethod target = ((StaticInvokeExpr)iexpr).getMethod();
		        if(!target.getName().equals("<init>"))
		        if(!Instrumenter.calls.contains(target.getName()))
		        		Instrumenter.calls.add(new String(target.getName()));
		    }

		}
		public void processFieldRef(Chain<Unit> newUnits, Stmt op){
			System.out.print(op.getFieldRef().getField());
			
		}
		
		public  DataType getType(soot.Value v) {

			Type type = v.getType();

			if(type instanceof BooleanType)
				return DataType.Boolean;

			if(type instanceof CharType)
				return DataType.Character;

			if(type instanceof PrimType)
				return DataType.Number;

			if(type instanceof RefType) {
				if(((RefType) type).getClassName().equals("java.lang.String"))
					return DataType.String;

				return DataType.Reference;
			}

			if(type instanceof NullType)
				return DataType.Reference;

			if(type instanceof ArrayType)
				return DataType.Array;

		//	if(TestFul.DEBUG) TestFul.debug("Unknown data type: " + type);


			return DataType.Reference;
		}
		    

		public void process(Chain<Unit> newUnits, IfStmt u) {
			


			ConditionExpr expr = (ConditionExpr) u.getCondition();

			soot.Value op1 = expr.getOp1();
			soot.Value op2 = expr.getOp2();
			typeOp1 = op1.getType();
			typeOp2 = op2.getType();

	//		args=typeOp1.toString();
//			System.out.println("type op1: "+ typeOp1);
//			System.out.println("type op2: "+ typeOp2);
			Stmt useStmt = u;
         System.out.println("trueBranch="+ trueBranch);
			DataType condDataType = getType(op1);
			isReturnTpeBool=false;
			args="";
			fieldRef_1="";
			fieldRef_2="";
			cValue_2="";
			argsField_2="";
			callee="";
			callee_1="";
			callee_2="";
			Value v1 = getValue(getRealUse(op1, useStmt, false));
			if(fieldRef_1.length()==0){
				fieldRef_1=fieldRef;
				cValue_1=cValue;
				callee_1=callee;
			}
		//	mClass=op1.getType().toString();
			argsField_1=args;
			fieldRef="";
			cValue="";
			callee="";

			if(!isReturnTpeBool){

				Value v2 = getValue(getRealUse(op2, useStmt, false));
				fieldRef_2=fieldRef;
				cValue_2=cValue;
				callee_2=callee;
				argsField_2=args;
			}
			String mType=methodPublic? "true":"false";
			newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setIfCondition.makeRef(), Arrays.asList(IntConstant.v(trueBranch),StringConstant.v(cName), StringConstant.v(mName),
					StringConstant.v(mType), StringConstant.v(fieldRef_1),StringConstant.v(fieldRef_2),
													StringConstant.v(cValue_1),StringConstant.v(cValue_2),
													StringConstant.v(callee_1),StringConstant.v(callee_2),
													StringConstant.v(mCall),StringConstant.v(mClass),StringConstant.v(args)  ))));
			newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setIfCondition.makeRef(), Arrays.asList(IntConstant.v(falseBranch),StringConstant.v(cName),StringConstant.v(mName),
					StringConstant.v(mType),StringConstant.v(fieldRef_1),StringConstant.v(fieldRef_2),
													StringConstant.v(cValue_1),StringConstant.v(cValue_2),
													StringConstant.v(callee_1),StringConstant.v(callee_2),
													StringConstant.v(mCall),StringConstant.v(mClass),StringConstant.v(args)))));
			fieldRef="";
			cValue=" ";
			mCall="";
			mClass="";
			args="";
			System.out.println("\n");
//			ConditionIf.ConditionType condType = null;
//			if(expr instanceof LtExpr) condType = ConditionType.LT;
//			if(expr instanceof LeExpr) condType = ConditionType.LE;
//			if(expr instanceof NeExpr) condType = ConditionType.NE;
//			if(expr instanceof EqExpr) condType = ConditionType.EQ;
//			if(expr instanceof GeExpr) condType = ConditionType.GE;
//			if(expr instanceof GtExpr) condType = ConditionType.GT;

//			ConditionIf c = new ConditionIf(mName, expr.toString(),  condDataType, v1, condType, v2);

//			c.setTrueBranch(trueBranch);
////			add(trueBranch, u.getTarget());
//
//			c.setFalseBranch(falseBranch);



			// the expression "if(expr)" is instrumented this way:
			//  1. if(expr) goto 10
			//  2. branchCovered(false)
			//  2a trackPUse(false, usesOfExpr)  { if PUse tracking}
			//  3. if(condTarget != trueBranch) goto 7
			//  4. trackDistance(op1, op2);
			//  5. goto 17
			//  7. if(contTarget != falseBranch) goto 17
			//  8. trackDistance(0);
			//  9. goto 17

			// 10. branchCovered(true)
			// 10a trackPUse(true, usesOfExpr)  { if PUse tracking}
			// 11. if(condTarget != trueBranch) goto 14
			// 12. trackDistance(0);
			// 13. goto 17
			// 14. if(contTarget != falseBranch) goto 17
			// 15. trackDistance(op1, op2);
			// 16. goto 17
			// 17. nop (after)

			Unit after = Jimple.v().newNopStmt();

			Unit handleTrue = Jimple.v().newNopStmt();
			
			newUnits.add(Jimple.v().newIfStmt(expr, handleTrue));
			{ // handle false

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(trueBranch))));

	//			newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, trackIfCondition.makeRef(), IntConstant.v(falseBranch),c)));
				
				// calculate distance (true)
				Unit nop = Jimple.v().newNopStmt();
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(trueBranch)), nop));
				if(typeOp1 instanceof BooleanType){
		//			newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(1))));
				}//if(type instanceof BooleanType){
				else if(typeOp1 instanceof IntegerType || typeOp1 instanceof LongType || typeOp1 instanceof FloatType || typeOp1 instanceof DoubleType) {
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1,
							(op1.getType() instanceof DoubleType) ? op1 : Jimple.v().newCastExpr(op1, DoubleType.v())));

					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2,
							(op2.getType() instanceof DoubleType) ? op2 : Jimple.v().newCastExpr(op2, DoubleType.v())));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));
				} else if(typeOp1 instanceof RefLikeType) {

					// localTmpDouble1 = (op1 == null) ? 0 : 1;
					Unit isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op1, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(1)));
					newUnits.add(isNull);

					// localTmpDouble2 = (op2 == null) ? 0 : 1;
					isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op2, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(1)));
					newUnits.add(isNull);
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));

				}
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newGotoStmt(after));
				// calculate distance (false)
				newUnits.add(nop);
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(falseBranch)), after));
		//		newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(-1))));
				newUnits.add(Jimple.v().newGotoStmt(after));
			}
			newUnits.add(handleTrue);
			{ // handle true
				// 10. branchCovered(true)
				// 10a trackPUse(true, usesOfExpr)  { if PUse tracking}
				// 11. if(condTarget != trueBranch) goto 14
				// 12. trackDistance(0);
				// 13. goto 17
				// 14. if(contTarget != falseBranch) goto 17
				// 15. trackDistance(op1, op2);
				// 16. goto 17
				// 17. nop (after)

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(trueBranch))));
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(falseBranch))));
				// calculate distance (true)
				Unit nop = Jimple.v().newNopStmt();
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(trueBranch)), nop));

		//		newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(-1))));
		
				newUnits.add(Jimple.v().newGotoStmt(after));

				// calculate distance (false)
				newUnits.add(nop);
	//			newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(falseBranch)), after));

				if(typeOp1 instanceof BooleanType) {
			//		newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(1))));
				}
				else if(typeOp1 instanceof IntegerType || typeOp1 instanceof LongType || typeOp1 instanceof FloatType || typeOp1 instanceof DoubleType) {
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1,
							(op1.getType() instanceof DoubleType) ? op1 : Jimple.v().newCastExpr(op1, DoubleType.v())));

					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2,
							(op2.getType() instanceof DoubleType) ? op2 : Jimple.v().newCastExpr(op2, DoubleType.v())));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));

				} else if(typeOp1 instanceof RefLikeType) {

					// localTmpDouble1 = (op1 == null) ? 0 : 1;
					Unit isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op1, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(1)));
					newUnits.add(isNull);

					// localTmpDouble2 = (op2 == null) ? 0 : 1;
					isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op2, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(1)));
					newUnits.add(isNull);
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));

				}
				newUnits.add(Jimple.v().newGotoStmt(after));
			}

			// done
			newUnits.add(after);
	//		current = null;
		}
		public void process(Chain<Unit> newUnits, LookupSwitchStmt u) {

			final soot.Value key = u.getKey();
			Value v1 = getValue(getRealUse(key, u, false));
			String mType=methodPublic? "true":"false";
			fieldRef_1=fieldRef;
			cValue_2="";
			argsField_1=args;
			fieldRef="";
			cValue="";
			callee="";
			
			List<IntConstant> lookupValues = new ArrayList<IntConstant>();
			List<Unit> targets = new ArrayList<Unit>();

			int[] keys = new int[u.getTargetCount()];
			SortedMap<Integer, Unit> keyTarget = new TreeMap<Integer, Unit>();
			Map<Integer, Integer> keyBranchId = new HashMap<Integer, Integer>();
			int i;
			for( i = 0; i < u.getTargetCount(); i++) {
				final int value = u.getLookupValue(i);
	
				String keyTpe=key.getType().toString();
				if(keyTpe.trim().equals("char")){
					char v=(char) value;
					cValue_2=""+ v; // returns ['A']
				}
				else
					cValue_2=""+ value;


				keys[i] = value;
				
				lookupValues.add(IntConstant.v(value));

				Unit stmt=u.getTarget(i);
				Unit defaultTarget=u.getDefaultTarget();
				int branchId= addBranch( stmt);
				
				keyBranchId.put(value, branchId);

				Unit target = Jimple.v().newNopStmt();
				targets.add(target);
				keyTarget.put(value, target);
				
				keyBranchId.put(value, branchId);
				Set<Integer> set = toLinkMap.get(stmt);
				if(!stmt.equals(defaultTarget)&& set.size()==1){
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(branchId))));
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setIfCondition.makeRef(), Arrays.asList(IntConstant.v(branchId),StringConstant.v(cName), StringConstant.v(mName),
							StringConstant.v(mType), StringConstant.v(fieldRef_1),StringConstant.v(fieldRef_2),
															StringConstant.v(cValue_1),StringConstant.v(cValue_2),
															StringConstant.v(callee_1),StringConstant.v(callee_2),
															StringConstant.v(mCall),StringConstant.v(mClass),StringConstant.v(argsField_1)  ))));
				}

			}

			Unit defaultTarget = Jimple.v().newNopStmt();
			Integer defaultBranchId;
			{
				defaultBranchId = addBranch(u.getDefaultTarget());
		
				String keyTpe=key.getType().toString();
				if(keyTpe.trim().equals("char")){
					char v =  characterGenerator.getCharacter();
					cValue_2=""+ v; // returns ['A']
				}
				else
					cValue_2=""+ ValuesGenerator.getRandomInt(1000);


				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(defaultBranchId))));
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setIfCondition.makeRef(), Arrays.asList(IntConstant.v(defaultBranchId),StringConstant.v(cName), StringConstant.v(mName),
						StringConstant.v(mType), StringConstant.v(fieldRef_1),StringConstant.v(fieldRef_2),
														StringConstant.v(cValue_1),StringConstant.v(cValue_2),
														StringConstant.v(callee_1),StringConstant.v(callee_2),
														StringConstant.v(mCall),StringConstant.v(mClass),StringConstant.v(argsField_1)  ))));
			}

			Unit lastNop = Jimple.v().newNopStmt();

			newUnits.add(Jimple.v().newLookupSwitchStmt(key, lookupValues, targets, defaultTarget));
			processSwitch(newUnits, u, key, keys, keyTarget, keyBranchId, defaultTarget, defaultBranchId, lastNop);
			newUnits.add(lastNop);

	//		current = null;
			
		}
		public void process(Chain<Unit> newUnits, TableSwitchStmt u) {
			final soot.Value key = u.getKey();
		
			Value v1 = getValue(getRealUse(key, u, false));
			String mType=methodPublic? "true":"false";
			fieldRef_1=fieldRef;
			cValue_2=cValue;
			argsField_1=args;
			fieldRef="";
			cValue="";
			callee="";
			
			final int lIndex = u.getLowIndex();
			final int hIndex = u.getHighIndex();
	
			int[] keys = new int[hIndex-lIndex+1];
			List<Unit> targets = new ArrayList<Unit>();
			SortedMap<Integer, Unit> keyTarget = new TreeMap<Integer, Unit>();
			Map<Integer, Integer> keyBranchId = new HashMap<Integer, Integer>();
	
			for(int idx = 0; idx <= hIndex-lIndex; idx++) {
				final int value = idx+lIndex;
				keys[idx] = value;
				String keyTpe=key.getType().toString();
				if(keyTpe.trim().equals("char")){
					char v=(char) value;
					cValue_2=""+ v; // returns ['A']
				}
				else
					cValue_2=""+ value;
				Unit stmt=u.getTarget(idx);
				Unit defaultTarget=u.getDefaultTarget();
			
				int branchId= addBranch( stmt);
	

				keyBranchId.put(value, branchId);
				Set<Integer> set = toLinkMap.get(stmt);
				if(!stmt.equals(defaultTarget)&& set.size()==1){
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(branchId))));
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setIfCondition.makeRef(), Arrays.asList(IntConstant.v(branchId),StringConstant.v(cName), StringConstant.v(mName),
							StringConstant.v(mType), StringConstant.v(fieldRef_1),StringConstant.v(fieldRef_2),
															StringConstant.v(cValue_1),StringConstant.v(cValue_2),
															StringConstant.v(callee_1),StringConstant.v(callee_2),
															StringConstant.v(mCall),StringConstant.v(mClass),StringConstant.v(argsField_1)  ))));

				}
				Unit target = Jimple.v().newNopStmt();
				targets.add(target);
				keyTarget.put(value, target);
			}

			Unit defaultTarget = Jimple.v().newNopStmt();
			Integer defaultBranchId;
			{
				defaultBranchId = addBranch(u.getDefaultTarget());
				String keyTpe=key.getType().toString();
				if(keyTpe.trim().equals("char")){
					char v=(char) (hIndex+1);
					cValue_2=""+ v; // returns ['A']
				}
				else
					cValue_2=""+ (hIndex+1);

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(defaultBranchId))));
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setIfCondition.makeRef(), Arrays.asList(IntConstant.v(defaultBranchId),StringConstant.v(cName), StringConstant.v(mName),
						StringConstant.v(mType), StringConstant.v(fieldRef_1),StringConstant.v(fieldRef_2),
														StringConstant.v(cValue_1),StringConstant.v(cValue_2),
														StringConstant.v(callee_1),StringConstant.v(callee_2),
														StringConstant.v(mCall),StringConstant.v(mClass),StringConstant.v(argsField_1)  ))));

			}

			Unit lastNop = Jimple.v().newNopStmt();

			newUnits.add(Jimple.v().newTableSwitchStmt(key, u.getLowIndex(), u.getHighIndex(), targets, defaultTarget));

			processSwitch(newUnits, u, key, keys, keyTarget, keyBranchId, defaultTarget, defaultBranchId, lastNop);
			newUnits.add(lastNop);
			
		}
	
		private int  addBranch( Unit stmt) {
			int newBranch=-1;
				
			Set<Integer> set = toLinkMap.get(stmt);
			if(set == null) {
				set = new HashSet<Integer>();
				toLinkMap.put(stmt, set);		

			}
			newBranch=branchId++;	
			set.add(newBranch);
				
			return newBranch;
		}
		private int  getBranchId( int branchId,int defaultBranch) {

			for (Map.Entry<Unit, Set<Integer>> entry : toLinkMap.entrySet()) {
				Unit stmt = entry.getKey();
				Set<Integer> set = entry.getValue();
			    if(set.contains(branchId)){
			    	if(set.contains(defaultBranch))
			    		return defaultBranch;
			    	for (Integer branch : branchUsedSwitch) 
			    		if(set.contains(branchUsedSwitch.iterator().next()))
			    			return branch;
			    }
			}
			branchUsedSwitch.add(branchId);
			return branchId;
		}

		private void processSwitch(Chain<Unit> newUnits, Unit switchUnit, soot.Value keyValue, int[] keys, SortedMap<Integer, Unit> keyTarget, Map<Integer, Integer> keyBranchId, Unit defaultTarget, int defaultBranchId, Unit lastNop) {
			for(final int key : keys) {
				final Unit start = keyTarget.get(key);
				final int branchId =getBranchId(keyBranchId.get(key), defaultBranchId);// keyBranchId.get(key);

				newUnits.add(start);
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(branchId))));

				// get the definition of the key

				// track distance

				// [0..i..n-1] i-th target; [n] -> default target;
				IntConstant[] ctlookupValues = new IntConstant[keys.length + 1];
				Unit[] ctTargets = new Unit[keys.length + 1];
				for (int i = 0; i < keys.length; i++) {
					 ctlookupValues[i] = IntConstant.v(keyBranchId.get(keys[i]));
			//		ctlookupValues[i] = IntConstant.v(getBranchId(keyBranchId.get(keys[i]), defaultBranchId));
					ctTargets[i] = Jimple.v().newNopStmt();
				}
				ctlookupValues[keys.length] =  IntConstant.v(defaultBranchId);
				ctTargets[keys.length] = Jimple.v().newNopStmt();
	
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[0])));
//
				newUnits.add(Jimple.v().newLookupSwitchStmt(localConditionTarget, Arrays.asList(ctlookupValues), Arrays.asList(ctTargets), lastNop));

				for(int i = 0; i < keys.length; i++) {
					final int ctKey = keys[i];
					newUnits.add(ctTargets[i]);
					//int BR=ctlookupValues[i];
	//				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[i])));
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(getBranchId(keyBranchId.get(keys[i]), defaultBranchId)))));

//					newUnits.add(Jimple.v().newLookupSwitchStmt(localConditionTarget, Arrays.asList(ctlookupValues), Arrays.asList(ctTargets), lastNop));

					if(ctKey == key) // branch executed!
						newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(-1.0))));
					else
						newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(Math.abs(ctKey - key)))));

//					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}

				{ // handle default target
					newUnits.add(ctTargets[keys.length]);
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[keys.length])));
//
//					newUnits.add(Jimple.v().newLookupSwitchStmt(localConditionTarget, Arrays.asList(ctlookupValues), Arrays.asList(ctTargets), lastNop));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(switchDistanceToDefault(key, keyBranchId.keySet())))));
					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}
			}

			// manage default target
			{
				newUnits.add(defaultTarget);
//				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(defaultBranchId))));
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(defaultBranchId))));



				IntConstant[] ctlookupValues = new IntConstant[keys.length + 1];
				Unit[] ctTargets = new Unit[keys.length + 1];
				for(int i = 0; i < keys.length; i++) {
					ctlookupValues[i] = IntConstant.v(keyBranchId.get(keys[i]));
				//	ctlookupValues[i] = IntConstant.v(getBranchId(keyBranchId.get(keys[i]), defaultBranchId));
					ctTargets[i] = Jimple.v().newNopStmt();
				}
				ctlookupValues[keys.length] =  IntConstant.v(defaultBranchId);
				ctTargets[keys.length] = Jimple.v().newNopStmt();
	
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[0])));
				newUnits.add(Jimple.v().newLookupSwitchStmt(localConditionTarget, Arrays.asList(ctlookupValues), Arrays.asList(ctTargets), lastNop));

				for(int i = 0; i < keys.length; i++) {
					final int ctKey = keys[i];
					newUnits.add(ctTargets[i]);
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, Jimple.v().newCastExpr(keyValue, DoubleType.v())));
//					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[i])));
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(getBranchId(keyBranchId.get(keys[i]), defaultBranchId)))));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(DoubleConstant.v(ctKey), localTmpDouble1))));
		//			newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}

				{ // handle default target
					newUnits.add(ctTargets[keys.length]);
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[keys.length])));
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(-1))));
					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}
			}
			System.out.print("");
		}
		private int switchDistanceToDefault(int key, Set<Integer> keySet) {
			if(!keySet.contains(key)) return 0;

			int d = 1;
			while(keySet.contains(key-d) && keySet.contains(key + d)) d++;

			return d;
		}


		private Data getData(soot.Value value) {

			if(value == null) return null;
			if(value instanceof Constant) return null;
			if(value instanceof Expr) return null;
//			if(value.equals(localThis)) return null;

			if(value instanceof Local) {
				Local local = (Local) value;

				Data ret = localRepository.get(local);
				if(ret == null) {
					ret = new Data(null, false);
					localRepository.put(local, ret);
				}
				return ret;
			}

			if(value instanceof FieldRef) return Factory.singleton.get(((FieldRef) value).getField());
			return null;
		}


		public Value getValue(soot.Value v) {
			if (v instanceof soot.jimple.Constant) {

				if(v instanceof NullConstant) return new Constant(null);
				if(v instanceof IntConstant) return new Constant(((IntConstant) v).value);
				if(v instanceof LongConstant) return new Constant(((LongConstant) v).value);
				if(v instanceof FloatConstant) return new Constant(((FloatConstant) v).value);
				if(v instanceof DoubleConstant) return new Constant(((DoubleConstant) v).value);
				if(v instanceof StringConstant) return null;

				return null;
			}

			if(v instanceof Local || v instanceof FieldRef)
				return getData(v);

			if(v instanceof Expr) return null;

			if(v instanceof ArrayRef) return null;

			return null;
		}
		
		public void processPost(Chain<Unit> newUnits, Stmt stmt) {

		}		



		private soot.Value getRealUse(soot.Value v, Unit u, boolean skipTrivial) {
			if(v == null) return null;


			if(u == null) return v;

			if(v instanceof Local) {

				List<Unit> reachingDefs = duAnalysis.getDefsOfAt((Local) v, u);

				if(reachingDefs.size() < 1) {
					if(skipTrivial) return null;
					return v;
				}

				// if the use has only one reaching definition, I can skip its tracking or check if it is a temporary variable
				if(reachingDefs.size() == 1) {

					// check if it is a temporary variable used to access a field
					Unit def = reachingDefs.get(0);

					if(def instanceof AssignStmt) {
						AssignStmt a = (AssignStmt)def;

						soot.Value rightOp = a.getRightOp();
						
						if((rightOp instanceof FieldRef) && duAnalysis.getUsesOf(def).size() == 1) {
			//				logger.info("Found temporary local " + v + " tracking uses of " + rightOp + " instead");
							return getRealUse(rightOp, def, skipTrivial);

						} else if(rightOp instanceof CastExpr) {
							soot.Value op = ((CastExpr) rightOp).getOp();
			//				logger.info("Found cast " + u + ": tracking uses of " + op + " instead");
							return getRealUse(op, def, skipTrivial);

						}else if(rightOp instanceof SpecialInvokeExpr){
							SpecialInvokeExpr fr = (SpecialInvokeExpr) rightOp;
							mCall=fr.getMethod().getName();
							mClass=fr.getMethodRef().declaringClass().getName();
							isReturnTpeBool= fr.getMethodRef().returnType().toString().equals("boolean")? true:false;
							System.out.println(" mCall="+ fr.getMethod().getName().toString()+" class:"+ fr.getMethodRef().declaringClass().getName());
						}else if(rightOp instanceof VirtualInvokeExpr ){
							VirtualInvokeExpr fr = (VirtualInvokeExpr) rightOp;
							soot.Value op1 = fr.getBase();
							isReturnTpeBool= fr.getMethodRef().returnType().toString().equals("boolean")? true:false;
							soot.Value v1 =  getRealUse(op1, def, skipTrivial);
							mCall=fr.getMethod().getName();
							if(fr.getArgCount()>0){
//								args=fr.getArg(0).toString();
								soot.Value arg=fr.getArg(0);
								getRealUse(arg, def, skipTrivial);
								args=cValue;
								cValue="";
							}
							System.out.println(" mCall="+ fr.getMethod().getName().toString() +" rturnTpe: "+ fr.getMethodRef().returnType() );
							}else
								if(rightOp instanceof InstanceFieldRef) {
									InstanceFieldRef fr = (InstanceFieldRef) rightOp;
									SootField field = fr.getField();
									fieldRef=field.getName().toString();
									mClass=fr.getFieldRef().type().toString();
									System.out.println("Field="+ fieldRef  +" Type:"+ mClass+ " Class:"+ fr.getFieldRef().declaringClass().getName());
									}else
										if(rightOp instanceof soot.jimple.internal.JCmpExpr ||rightOp instanceof soot.jimple.internal.JCmpgExpr){
											soot.Value op1,op2;
											if(rightOp instanceof soot.jimple.internal.JCmpExpr){
												soot.jimple.internal.JCmpExpr fr=(soot.jimple.internal.JCmpExpr) rightOp;
												op1 = fr.getOp1();
												op2 = fr.getOp2();
											}else{
												soot.jimple.internal.JCmpgExpr fr=(soot.jimple.internal.JCmpgExpr) rightOp;
												op1 = fr.getOp1();
												op2 = fr.getOp2();
											}
											soot.Value v1 =  getRealUse(op1, def, skipTrivial);
											if(fieldRef.length()!=0){
												fieldRef_1=fieldRef;
												callee_1=callee;
												cValue_1=cValue;
											}
											else
												if(fieldRef_1.length()==0){
													fieldRef_1=args;
													callee_1=callee;
												cValue_1=cValue;
												}
											fieldRef="";
											args="";
											cValue="";
											callee="";

											soot.Value v2 =  getRealUse(op2, def, skipTrivial);
											if(fieldRef.length()!=0){
												fieldRef_2=fieldRef;
												callee_2=callee;
											}
											else
												fieldRef_2=args;
											callee_2=callee;
											cValue_2=cValue;
											fieldRef="";
											callee="";
											args="";
											cValue="";
											isReturnTpeBool=true;
										}else 
											if(rightOp instanceof soot.jimple.internal.JStaticInvokeExpr){
												soot.jimple.internal.JStaticInvokeExpr fr = (soot.jimple.internal.JStaticInvokeExpr) rightOp;
												mCall=fr.getMethod().getName();
												mClass=fr.getMethodRef().declaringClass().getName();
												if(fr.getArgCount()>0){
													args=fr.getArg(0).toString();
													soot.Value arg=fr.getArg(0);
													getRealUse(arg, def, skipTrivial);
													args=fieldRef;
													fieldRef="";
												}
												System.out.println(" mCall="+ fr.getMethod().getName().toString()+ " Type:"+ fr.getMethodRef().declaringClass().getName() +" args: "+args );

											}else
												if( rightOp instanceof soot.jimple.internal.JInterfaceInvokeExpr){
													soot.jimple.internal.JInterfaceInvokeExpr fr = (soot.jimple.internal.JInterfaceInvokeExpr) rightOp;
													soot.Value op = fr.getBase();
													getRealUse(op, def, skipTrivial);
													if(fieldRef.length()!=0){
														fieldRef_1=fieldRef;
														callee_1=callee;
													}
													callee="";
													fieldRef="";
													isReturnTpeBool= fr.getMethodRef().returnType().toString().equals("boolean")? true:false;
													if(fr.getArgCount()!=0){
														soot.Value v1=fr.getArg(0);
														getRealUse(v1, def, skipTrivial);
														if(fieldRef.length()!=0){
															fieldRef_2=fieldRef;
															callee_2=callee;
														}

													}
//													System.out.print(fr);

													
												}else
													if( rightOp instanceof soot.jimple.internal.JAndExpr||rightOp instanceof soot.jimple.internal.JOrExpr){
														soot.jimple.internal.JAndExpr fr = (soot.jimple.internal.JAndExpr) rightOp;
														soot.Value op1 = fr.getOp1();
														soot.Value v1 =  getRealUse(op1, def, skipTrivial);
														soot.Value op2 = fr.getOp2();
														soot.Value v2 =  getRealUse(op2, def, skipTrivial);
													System.out.println(fr);

													}else
														if( rightOp instanceof soot.jimple.internal.JSubExpr||rightOp instanceof soot.jimple.internal.JAddExpr){
															soot.Value op1,op2;
															if(rightOp instanceof soot.jimple.internal.JSubExpr){
																soot.jimple.internal.JSubExpr fr = (soot.jimple.internal.JSubExpr) rightOp;
																System.out.println(fr);
																 op1 = fr.getOp1();
																 op2 = fr.getOp2();
															}
															else{
																soot.jimple.internal.JAddExpr fr= (soot.jimple.internal.JAddExpr) rightOp;
																System.out.println(fr);
																op1 = fr.getOp1();
																 op2 = fr.getOp2();
															}

															soot.Value v1 =  getRealUse(op1, def, skipTrivial);
															
															soot.Value v2 =  getRealUse(op2, def, skipTrivial);
														}else if(rightOp instanceof soot.jimple.internal.JInstanceOfExpr){
															soot.jimple.internal.JInstanceOfExpr fr= (soot.jimple.internal.JInstanceOfExpr) rightOp;
															soot.Value op1 = fr.getOp();
															 getRealUse(op1, def, skipTrivial);
																if(fieldRef.length()!=0){
																	fieldRef_1=fieldRef;
																	callee_1=callee;
																	cValue_1=fr.getCheckType().toString();
																}
																callee="";
																fieldRef="";
																isReturnTpeBool= fr.getType().toString().equals("boolean")? true:false;
//															System.out.println(" instance of: " + v1.ge);
														}
														else if(rightOp instanceof JArrayRef) {
															soot.jimple.internal.JArrayRef fr = (soot.jimple.internal.JArrayRef) rightOp;
															soot.Value op1 = fr.getBase();
															 getRealUse(op1, def, skipTrivial);
																if(fieldRef_1.length()==0){
																	fieldRef_1=fieldRef;
																	callee_1=callee;
																	cValue_1="";
																}else{
																	fieldRef_2=fieldRef;
																	callee_2=callee;
																	cValue_2="";																	
																}
																callee="";
																fieldRef="";
																isReturnTpeBool= fr.getType().toString().equals("boolean")? true:false;

															//System.out.println(" instance of: " + rightOp);
															}
															else{
																System.out.println("\t-------------------------------");
																System.out.println("\tWe need to analysis: "+rightOp.toString() );
																System.out.println("\t-------------------------------");
															}

					}else if(def instanceof IdentityStmt) {
						IdentityStmt stmt = (IdentityStmt)def;
						final soot.Value leftOp = stmt.getLeftOp();
						soot.Value rightOp = stmt.getRightOp();
						fieldRef=rightOp.toString();
						mClass= rightOp.getType().toString();
						soot.Value calee = stmt.getRightOp();
						callee=getRealUseObject(calee, u, skipTrivial);
						System.out.println(" Parameter= "+ fieldRef +" Type= " + mClass);

					}else if(def instanceof soot.jimple.internal.JAssignStmt) {
						soot.jimple.internal.JAssignStmt stmt = (soot.jimple.internal.JAssignStmt)def;
						System.out.println(" Parameter="+ fieldRef +" Type= ");
					}

					if(skipTrivial) {
						logger.info("Skipping instrumentation of use of " + v + " in " + u + ": only 1 reachable def-use");
						return null;
					}

					return v;
				}
			}

			if(v instanceof InstanceFieldRef) {
				Unit def;
				InstanceFieldRef fr = (InstanceFieldRef) v;
				SootField field = fr.getField();
				fieldRef=field.getName().toString();
				mClass=fr.getFieldRef().type().toString();
				isReturnTpeBool= fr.getType().toString().equals("boolean")? true:false;
				soot.Value calee = fr.getBase();
				callee=getRealUseObject(calee, u, skipTrivial);
				System.out.println(" field="+ field.getName()+ " Type:"+ mClass+ " class:"+ fr.getFieldRef().declaringClass().getName()+ " callee: " + callee);
			}else if(v instanceof StaticFieldRef) {
				StaticFieldRef fr = (StaticFieldRef) v;
				SootField field = fr.getField();
				fieldRef=field.getName();
				mClass=fr.getFieldRef().type().toString();
				System.out.println(" StaticFieldRef="+ field.getName()+ " Type:"+ mClass+ " class:"+ fr.getFieldRef().declaringClass().getName());
			}else if(v instanceof NullConstant) {
				NullConstant fr = (NullConstant) v;
				cValue="null";
				System.out.println(" NullConstant="+fr.v());
			}else if(v instanceof IntConstant) {
				IntConstant fr = (IntConstant) v;
				if(typeOp1 instanceof CharType)
					cValue=""+ (char)fr.value; //Character.toChars(fr.value)[0];
				else
					cValue=""+fr.value;
				System.out.println(" IntConstant="+cValue);
			}else if(v instanceof JimpleLocal) {
				JimpleLocal fr = (JimpleLocal) v;
				List<Unit> reachingDefs = duAnalysis.getDefsOfAt((Local) v, u);
				for(int i=0;i<reachingDefs.size();i++){
				Unit def = reachingDefs.get(i);
				if(def instanceof AssignStmt) {
					AssignStmt a = (AssignStmt)def;
					soot.Value rightOp = a.getRightOp();				
					System.out.print("\n\t **Skipping instrumentation of the definition of parameter="+rightOp);
				}else if(def instanceof IdentityStmt) {
					IdentityStmt stmt = (IdentityStmt)def;
					final soot.Value leftOp = stmt.getLeftOp();
					soot.Value rightOp = stmt.getRightOp();
					fieldRef=rightOp.toString();
					String paramTpe= rightOp.getType().toString();
			//		Scene.v().loadClassAndSupport(paramTpe);
			//		SootClass sClass = Scene.v().getSootClass(paramTpe);
			//		sClass.getField(paramTpe);
					System.out.println(" Parameter= "+ fieldRef +" Type= " + paramTpe);
					break;
				}
				}
			}else if(v instanceof soot.jimple.StringConstant){
				StringConstant fr = (StringConstant) v;
				cValue_2=""+fr.value;
				System.out.println(" String Constant= "+ cValue_2);
			}else if(v instanceof soot.jimple.DoubleConstant){
				DoubleConstant fr = (DoubleConstant) v;
				cValue=""+fr.value;
				System.out.println(" Double Constant = "+ cValue);
			}

				
			return v;
		}
		private String	   getRealUseObject(soot.Value v, Unit u, boolean skipTrivial) {

			String calleeObj=null;
			if(v == null) return calleeObj;
			if(u == null) return calleeObj;

			if(v instanceof Local) {

				List<Unit> reachingDefs = duAnalysis.getDefsOfAt((Local) v, u);

				if(reachingDefs.size() < 1) {
					if(skipTrivial) return calleeObj;
					return calleeObj;
				}

				// if the use has only one reaching definition, I can skip its tracking or check if it is a temporary variable
				if(reachingDefs.size() == 1) {

					// check if it is a temporary variable used to access a field
					Unit def = reachingDefs.get(0);

					if(def instanceof AssignStmt) {
						AssignStmt a = (AssignStmt)def;

						soot.Value rightOp = a.getRightOp();
						
						if((rightOp instanceof FieldRef) ){//&& duAnalysis.getUsesOf(def).size() == 1) {
//							logger.info("Found temporary local " + v + " tracking uses of " + rightOp + " instead");
							 getRealUseObject(rightOp, def, skipTrivial);
					}else if(rightOp instanceof CastExpr) {
						soot.Value op = ((CastExpr) rightOp).getOp();
//						logger.info("Found cast " + u + ": tracking uses of " + op + " instead");
						 getRealUseObject(op, def, skipTrivial);

					}
				}else if(def instanceof IdentityStmt) {
					IdentityStmt stmt = (IdentityStmt)def;
					soot.Value rightOp = stmt.getRightOp();
					callee=rightOp.toString();
					//mClass= rightOp.getType().toString();
					
					System.out.println("\n callee= "+ callee );
				}
			}
			}
			if(v instanceof InstanceFieldRef) {
				Unit def;
				InstanceFieldRef fr = (InstanceFieldRef) v;
				SootField field = fr.getField();
				soot.Value calee = fr.getBase();
				callee=getRealUseObject(calee, u, skipTrivial);
		//		System.out.println(" field="+ field.getName()+ " Type:"+ mClass+ " class:"+ fr.getFieldRef().declaringClass().getName()+ " callee: " + callee);
			}else if(v instanceof soot.jimple.ParameterRef) 
						callee=v.toString();
			return callee;		
		   }
		public void process(Chain<Unit> newUnits, DefinitionStmt stmt) {
//			final soot.Value leftOp = stmt.getLeftOp();
//			soot.Value rightOp = stmt.getRightOp();
//			if(leftOp instanceof Local && !(leftOp.getType() instanceof ArrayType)) {
//
//				// Consider the uses of the current definitions: if they do not have any additional definition, then it is possible to skip the du tracking
//				boolean oneDef = true;
//				@SuppressWarnings("unchecked")
//				List<UnitValueBoxPair> usesOf = duAnalysis.getUsesOf(stmt);
//				for (UnitValueBoxPair unit : usesOf) {
//					if(duAnalysis.getDefsOfAt((Local) leftOp, unit.getUnit()).size() > 1) {
//						oneDef = false;
//						break;
//					}
//				}
//				if(oneDef) {
//					System.out.println(" WhiteInstrumenter.process Skipping instrumentation of the definition of " + leftOp + " in " + stmt + ": it is the only definition for all of its uses");
//					return;
//				}
//			}
//
//
		}
		private void getTrackingDef( soot.Value v, Unit u) {
			boolean flag=true;
			if(flag){
				v = getRealUse(v, u, true);
				flag=false;
			}
//			if(v == null) return null;

//			if(v instanceof Local)
//				return getTrackingLocal((Local) v);

			if(v instanceof InstanceFieldRef) {

				try {
					InstanceFieldRef fr = (InstanceFieldRef) v;
					SootField field = fr.getField();
			//		SootField tracker = field.getDeclaringClass().getFieldByName(getTracker(field.getName()));
					System.out.println("parameter ="+ field.getName()+ " class:"+ fr.getFieldRef().declaringClass().getName());

//					newUnits.add(Jimple.v().newAssignStmt(localDataAccessD, Jimple.v().newInstanceFieldRef(fr.getBase(), tracker.makeRef())));
//
//					return localDataAccessD;

				} catch (RuntimeException e) {
					logger.log(Level.WARNING, "Tracking field not found: " + e.getMessage(), e);
				}

			}

			if (v instanceof StaticFieldRef) {

				try {
					StaticFieldRef fr = (StaticFieldRef) v;
					SootField field = fr.getField();
//					SootField tracker = field.getDeclaringClass().getFieldByName(getTracker(field.getName()));
					System.out.println("Parameter="+ field.getName()+ " class:"+ fr.getFieldRef().declaringClass().getName());

//					newUnits.add(Jimple.v().newAssignStmt(localDataAccessD, Jimple.v().newStaticFieldRef(tracker.makeRef())));

//					return localDataAccessD;

				} catch (RuntimeException e) {
					logger.log(Level.WARNING, "Tracking field not found: " + e.getMessage(), e);
				}

			}

		}
		public void process(Chain<Unit> newUnits, GotoStmt u) {
//			add(new EdgeDirect(current), u.getTarget());
//			current = null;
		}

		public void process(Chain<Unit> newUnits, IdentityStmt u) {

		}
		public void process(Chain<Unit> newUnits, RetStmt u) {

//			EdgeDirect.create(current, end);
//			current = null;
		}

		public void process(Chain<Unit> newUnits, ReturnStmt u) {

//			EdgeDirect.create(current, end);
//			current = null;
		}

		public void process(Chain<Unit> newUnits, ReturnVoidStmt u) {

//			EdgeDirect.create(current, end);
//			current = null;
		}

		public void process(Chain<Unit> newUnits, ThrowStmt u) {
			SootClass exc = ((RefType) u.getOp().getType()).getSootClass();

			boolean handled = false;
//			if(!handled) EdgeExceptional.create(current, end, exc.getJavaStyleName());
////
//			current = null;
		}


	}

	}


